/******************************************************************************
*	usbd/usbd.c
*	 by Alex Chadwick
*
*	A light weight implementation of the USB protocol stack fit for a simple
*	driver.
*
*	usbd/usbd.c contains code relating to the generic USB driver. USB
*	is designed such that this driver's interface would be virtually the same
*	across all systems, and in fact its implementation varies little either.
******************************************************************************/
#include <hcd/hcd.h>
#include <platform/platform.h>
#include <usbd/descriptors.h>
#include <usbd/usbd.h>

#define ControlMessageTimeout  (10)  // Default timeout for control transfers (mS)
#define MaximumDevices         (32)  // Maximum number of devices that can be connected

UsbDevice  *Devices[MaximumDevices];
Result     (*InterfaceClassAttach[InterfaceClassAttachCount])(UsbDevice *device, u32 interfaceNumber);

void  ConfigurationLoad (void) ;
Result  UsbAttachRootHub (void) ;

//+=============================================================================
// Func: UsbGetRootHub
// Desc: Returns a pointer to the root hub device.
// Args: -none-
// Retn: UsbDevice* ..
// Note: On a Universal Serial Bus, there exists a root hub.
//     : This if often a virtual device, and typically represents
//         a one port hub, which is the physical USB for this computer.
//     : It is always address 1.
//     : It is present to allow uniform software manipulation of the USB
// Auth: Chadderz; BlueChip
//==============================================================================
UsbDevice*  UsbGetRootHub (void)
{
	return Devices[0];
}

//+=============================================================================
// Func: UsbLoad
// Desc:
// Args: -none-
// Retn: -none-
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
void  UsbLoad (void)
{
	u32  i;

	LOG("CSUD: USB driver version 0.1\n");

	for (i = 0; i < MaximumDevices;            i++)  Devices[i] = NULL ;
	for (i = 0; i < InterfaceClassAttachCount; i++)  InterfaceClassAttach[i] = NULL ;

	return;
}

//+=============================================================================
// Func: UsbInitialise
// Desc: Initialise the USB driver.
// Args: -none-
// Retn: Result ..
// Note: Performing all necessary interfactions with the host controller driver
//         and enumerate the device tree.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbInitialise (void)
{
	Result  result = OK;

	ConfigurationLoad();

	if (sizeof(UsbDeviceRequest) != 0x8) {
		LOGF("USBD: Incorrectly compiled driver. "
		     "UsbDeviceRequest: %#x (0x8).\n", sizeof(UsbDeviceRequest));
		return ErrorCompiler; // Correct packing settings are required.
	}

	result = HcdInitialise();
	if (result != OK) {
		LOG("USBD: Abort, HCD failed to initialise.\n");
		goto errorReturn;
	}

	result = HcdStart();
	if (result != OK) {
		LOG("USBD: Abort, HCD failed to start.\n");
		goto errorDeinitialise;
	}

	result = UsbAttachRootHub();
	if (result != OK) {
		LOG("USBD: Failed to enumerate devices.\n");
		goto errorStop;
	}

	return result;

errorStop:
	HcdStop();

errorDeinitialise:
	HcdDeinitialise();

errorReturn:
	return result;
}

//+=============================================================================
// Func: UsbControlMessage
// Desc: Sends a control message synchronously to a given device.
// Args: UsbDevice*        device       ..
//     : UsbPipeAddress    pipe         ..
//     : void*             buffer       ..
//     : u32               bufferLength ..
//     : UsbDeviceRequest* request      ..
//     : u32               timeout      ..
// Retn: Result ..
// Note: After sending the message we wait for completion.
//         If the timeout is reached we return ErrorTimeout.
//     : This function puts the device into an inconsistent state,
//         so best not to use it until processing is unset.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbControlMessage (UsbDevice* device, UsbPipeAddress pipe,
                           void* buffer, u32 bufferLength,
                           UsbDeviceRequest* request, u32 timeout)
{
	Result  result;

	if (((u32)buffer & 0x3) != 0)
		LOG_DEBUG("USBD: Warning message buffer not word aligned.\n");

	result = HcdSumbitControlMessage(device, pipe, buffer, bufferLength, request);
	if (result != OK) {
		LOG_DEBUGF("USBD: Failed to send message to %s: %d.\n", UsbGetDescription(device), result);
		return result;
	}

	while ((timeout-- > 0) && (device->Error & Processing))  MicroDelay(1000) ;

	if (device->Error & Processing) {
		LOG_DEBUGF("USBD: Message to %s timeout reached.\n", UsbGetDescription(device));
		return ErrorTimeout;
	}

	if (device->Error & ~Processing) {
		if ((device->Parent != NULL) && (device->Parent->DeviceCheckConnection != NULL)) {
			// Check we're still connected!
			LOG_DEBUGF("USBD: Verifying %s is still connected.\n", UsbGetDescription(device));
			result = device->Parent->DeviceCheckConnection(device->Parent, device);
			if (result != OK)  return ErrorDisconnected ;
			LOG_DEBUGF("USBD: Yes, %s is still connected.\n", UsbGetDescription(device));
		}
		result = ErrorDevice;
	}

	return result;
}

//+=============================================================================
// Func: UsbGetDescriptor
// Desc: Get the descriptor for a device based on the index and language ID
// Args: UsbDevice*     device        ..
//     : DescriptorType type          ..
//     : u8             index         ..
//     : u16            langId        ..
//     : void*          buffer        ..
//     : u32            length        ..
//     : u32            minimumLength ..
//     : u8             minimumLength ..
// Retn: Result .. [1]
// Note: [1] The returned value is not longer than length.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbGetDescriptor (UsbDevice* device, DescriptorTYPE type,
                          u8 index, u16 langId, void* buffer, u32 length,
                          u32 minimumLength, u8 recipient)
{
	Result            result;
	UsbPipeAddress    pipe;
	UsbDeviceRequest  dreq;

	pipe.Type      = Control;
	pipe.Speed     = device->Speed;
	pipe.EndPoint  = 0;
	pipe.Device    = device->Number;
	pipe.Direction = In;
	pipe.MaxSize   = SizeFromNumber(device->Descriptor.MaxPacketSize0);

	dreq.Request = GetDescriptor;
	dreq.Type    = 0x80 | recipient;
	dreq.Value   = ((u16)type << 8) | index;
	dreq.Index   = langId;
	dreq.Length  = length;

	result = UsbControlMessage(device, pipe, buffer, length, &dreq, ControlMessageTimeout);
	if (result != OK) {
		LOGF("USBD: Failed to get descriptor %#x:%#x for device %s. Result %#x.\n",
		     type, index, UsbGetDescription(device), result);
		return result;
	}

	if (device->LastTransfer < minimumLength) {
		LOGF("USBD: Unexpectedly short descriptor (%d/%d) %#x:%#x for device %s. Result %#x.\n",
		     device->LastTransfer, minimumLength, type, index, UsbGetDescription(device), result);
		return ErrorDevice;
	}

	return OK;
}

//+=============================================================================
// Func: UsbGetString
// Desc:
// Args: UsbDevice* device      ..
//     : u8         stringIndex ..
//     : u16        langId      ..
//     : void*      buffer      ..
//     : u32        length      ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbGetString (UsbDevice* device, u8 stringIndex, u16 langId,
                      void* buffer, u32 length)
{
	u32     i;
	Result  result;

//! Apparently this tends to fail a lot.
	for (i = 0; i < 3; i++) {
		result = UsbGetDescriptor(device, String, stringIndex, langId, buffer, length, length, 0);
		if (result == OK)  break ;
	}

	return result;
}

//+=============================================================================
// Func: UsbReadStringLang
// Desc:
// Args: UsbDevice* device      ..
//     : u8         stringIndex ..
//     : u16        langId      ..
//     : void*      buffer      ..
//     : u32        length      ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbReadStringLang (UsbDevice* device, u8 stringIndex, u16 langId,
                           void* buffer, u32 length)
{
	Result  result;

	result = UsbGetString(device, stringIndex, langId, buffer, Min(2, length, u32));
	if ((result == OK) && (device->LastTransfer != length))
		result = UsbGetString(device, stringIndex, langId, buffer, Min(((u8*)buffer)[0], length, u32));

	return result;
}

//+=============================================================================
// Func: UsbReadString
// Desc:
// Args: UsbDevice* device      ..
//     : u8         stringIndex ..
//     : char*      buffer      ..
//     : u32        length      ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbReadString (UsbDevice* device, u8 stringIndex, char* buffer,
                       u32 length)
{
	Result               result;
	u32                  i;
	u8                   descriptorLength;
	u16                  langIds[2];
	UsbStringDescriptor  *descriptor;

	if ((buffer == NULL) || (stringIndex == 0))  return ErrorArgument ;

	result = UsbReadStringLang(device, 0, 0, &langIds, 4);
	if (result != OK) {
		LOGF("USBD: Error getting language list from %s.\n", UsbGetDescription(device));
		return result;

	} else if (device->LastTransfer < 4) {
		LOGF("USBD: Unexpectedly short language list from %s.\n", UsbGetDescription(device));
		return ErrorDevice;
	}

	descriptor = (UsbStringDescriptor*)buffer;
	if (descriptor == NULL)  return ErrorMemory ;

	result = UsbReadStringLang(device, stringIndex, langIds[1], descriptor, length);
	if (result != OK)  return result ;

	descriptorLength = descriptor->DescriptorLength;
	for (i = 0; (i < length - 1) && (i < (descriptorLength - 2) >> 1); i++)
		if (descriptor->Data[i] > 0xff)  buffer[i] = '?' ;
		else                             buffer[i] = descriptor->Data[i] ;

	if (i < length)  buffer[i++] = '\0' ;

	return result;
}

//+=============================================================================
// Func: UsbReadDeviceDescriptor
// Desc:
// Args: UsbDevice* device ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbReadDeviceDescriptor (UsbDevice* device)
{
	Result  result;

	if (device->Speed == Low) {
		device->Descriptor.MaxPacketSize0 = 8;

		result = UsbGetDescriptor(device, Device, 0, 0, (void*)&device->Descriptor,
		                          sizeof(device->Descriptor), 8, 0);
		if (result != OK)  return result ;

		if (device->LastTransfer == sizeof(UsbDeviceDescriptor))  return result ;

	} else if (device->Speed == Full) {
		device->Descriptor.MaxPacketSize0 = 64;

		result = UsbGetDescriptor(device, Device, 0, 0, (void*)&device->Descriptor,
		                          sizeof(device->Descriptor), 8, 0);
		if (result != OK)  return result ;

		if (device->LastTransfer == sizeof(UsbDeviceDescriptor))  return result ;

	} else {
		device->Descriptor.MaxPacketSize0 = 64;
	}

	result = UsbGetDescriptor(device, Device, 0, 0, (void*)&device->Descriptor,
	                          sizeof(device->Descriptor), sizeof(device->Descriptor), 0);

	return result;
}

//+=============================================================================
// Func: UsbSetAddress
// Desc:
// Args: UsbDevice* device  ..
//     : u8         address ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbSetAddress (UsbDevice* device, u8 address)
{
	Result            result;
	UsbPipeAddress    pipe;
	UsbDeviceRequest  dreq;

	if (device->Status != Default) {
		LOGF("USBD: Illegal attempt to configure device %s in state %#x.\n",
		     UsbGetDescription(device), device->Status);
		return ErrorDevice;
	}

	pipe.Type      = Control;
	pipe.Speed     = device->Speed;
	pipe.EndPoint  = 0;
	pipe.Device    = 0;
	pipe.Direction = Out;
	pipe.MaxSize   = SizeFromNumber(device->Descriptor.MaxPacketSize0);

	dreq.Request = SetAddress;
	dreq.Type    = 0;
	dreq.Value   = address;

	result = UsbControlMessage(device, pipe, NULL, 0, &dreq, ControlMessageTimeout);
	if (result != OK)  return result ;

	MicroDelay(10000);  // Allow the address to propagate.

	device->Number = address;
	device->Status = Addressed;

	return OK;
}

//+=============================================================================
// Func: UsbSetConfiguration
// Desc:
// Args: UsbDevice* device        ..
//     : u8         configuration ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbSetConfiguration (UsbDevice* device, u8 configuration)
{
	Result            result;
	UsbPipeAddress    pipe;
	UsbDeviceRequest  dreq;

	if (device->Status != Addressed) {
		LOGF("USBD: Illegal attempt to configure device %s in state %#x.\n",
		     UsbGetDescription(device), device->Status);
		return ErrorDevice;
	}

	pipe.Type      = Control;
	pipe.Speed     = device->Speed;
	pipe.EndPoint  = 0;
	pipe.Device    = device->Number;
	pipe.Direction = Out;
	pipe.MaxSize   = SizeFromNumber(device->Descriptor.MaxPacketSize0);

	dreq.Request = SetConfiguration;
	dreq.Type    = 0;
	dreq.Value   = configuration;

	result = UsbControlMessage(device, pipe, NULL, 0, &dreq, ControlMessageTimeout);
	if (result != OK)  return result ;

	device->ConfigurationIndex = configuration;
	device->Status             = Configured;

	return OK;
}

//+=============================================================================
// Func: UsbConfigure
// Desc:
// Args: UsbDevice* device        ..
//     : u8         configuration ..
// Retn: Result ..
// Note:
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbConfigure (UsbDevice* device, u8 configuration)
{
	Result                  result;
	void*                   fullDescriptor;
	UsbDescriptorHeader     *header;
	UsbInterfaceDescriptor  *interface;
	UsbEndpointDescriptor   *endpoint;
	u32                     lastInterface, lastEndpoint;
	bool                    isAlternate;

	if (device->Status != Addressed) {
		LOGF("USBD: Illegal attempt to configure device %s in state %#x.\n",
		     UsbGetDescription(device), device->Status);
		return ErrorDevice;
	}

	result = UsbGetDescriptor(device, Configuration, configuration, 0,
	                          (void*)&device->Configuration, sizeof(device->Configuration),
	                          sizeof(device->Configuration), 0);
	if (result != OK) {
		LOGF("USBD: Failed to retrieve configuration descriptor %#x for device %s.\n",
		     configuration, UsbGetDescription(device));
		return result;
	}

	fullDescriptor = MemoryAllocate(device->Configuration.TotalLength);
	if (fullDescriptor == NULL) {
		LOG("USBD: Failed to allocate space for descriptor.\n");
		return ErrorMemory;
	}

	result = UsbGetDescriptor(device, Configuration, configuration, 0,
	                          fullDescriptor, device->Configuration.TotalLength,
	                          device->Configuration.TotalLength, 0);
	if (result != OK) {
		LOGF("USBD: Failed to retrieve full configuration descriptor %#x for device %s.\n",
		     configuration, UsbGetDescription(device));
		goto deallocate;
	}

	device->ConfigurationIndex = configuration;
	configuration              = device->Configuration.ConfigurationValue;

	header        = fullDescriptor;
	lastInterface = MaxInterfacesPerDevice;
	lastEndpoint  = MaxEndpointsPerDevice;
	isAlternate   = false;

	for (header = (UsbDescriptorHeader*)((u8*)header + header->DescriptorLength);
	     ((u32)header - (u32)fullDescriptor) < device->Configuration.TotalLength;
	     header = (UsbDescriptorHeader*)((u8*)header + header->DescriptorLength)) {
		switch (header->DescriptorType) {
			case Interface:
				interface = (UsbInterfaceDescriptor*)header;
				if (lastInterface != interface->Number) {
					MemoryCopy((void*)&device->Interfaces[lastInterface = interface->Number],
					           (void*)interface, sizeof(UsbInterfaceDescriptor));
					lastEndpoint = 0;
					isAlternate  = false;
				} else {
					isAlternate  = true;
				}
				break;

			case Endpoint:
				if (isAlternate) break;
				if ((lastInterface == MaxInterfacesPerDevice) ||
				    (lastEndpoint >= device->Interfaces[lastInterface].EndpointCount)) {
					LOG_DEBUGF("USBD: Unexpected endpoint descriptor in %s.Interface%d.\n",
					           UsbGetDescription(device), lastInterface + 1);
					break;
				}
				endpoint = (UsbEndpointDescriptor*)header;
				MemoryCopy((void*)&device->Endpoints[lastInterface][lastEndpoint++],
				           (void*)endpoint, sizeof(UsbEndpointDescriptor));
				break;

			default:
				if (header->DescriptorLength == 0)  goto headerLoopBreak ;

				break;
		}

		LOG_DEBUGF("USBD: Descriptor %d length %d, interface %d.\n",
		           header->DescriptorType, header->DescriptorLength, lastInterface);
	}
headerLoopBreak:

	result = UsbSetConfiguration(device, configuration);
	if (result != OK)  goto deallocate ;

	LOG_DEBUGF("USBD: %s configuration %d. Class %d, subclass %d.\n",
	           UsbGetDescription(device), configuration,
	           device->Interfaces[0].Class, device->Interfaces[0].SubClass);

	device->FullConfiguration = fullDescriptor;

	return OK;

deallocate:
	MemoryDeallocate(fullDescriptor);
	return result;
}

//+=============================================================================
// Func: UsbGetDescription
// Desc: Returns a description for a device.
// Args: UsbDevice* device ..
// Retn: const char* ..
// Note: This is not read from the device, it is generated by the driver.
// Auth: Chadderz; BlueChip
//==============================================================================
const char*  UsbGetDescription (UsbDevice* device)
{
	if      (device->Status == Attached)  return "New Device (Not Ready)\0" ;
	else if (device->Status == Powered)   return "Unknown Device (Not Ready)\0" ;
	else if (device == Devices[0])        return "USB Root Hub\0" ;

	switch (device->Descriptor.Class) {
		case DeviceClassHub:
			switch (device->Descriptor.UsbVersion) {
				case 0x210:  return "USB 2.1 Hub\0" ;
				case 0x200:  return "USB 2.0 Hub\0" ;
				case 0x110:  return "USB 1.1 Hub\0" ;
				case 0x100:  return "USB 1.0 Hub\0" ;
				default:     return "USB Hub\0" ;
			}

		case DeviceClassVendorSpecific:
			// Try to identify the specific device
			if ((device->Descriptor.VendorId  == 0x0424) &&
				(device->Descriptor.ProductId == 0xEC00))
				return "SMSC LAN9512\0";
			// else fallthrough to DeviceClassInInterface

		case DeviceClassInInterface:
			if (device->Status == Configured) {
				// Identify the device by its class
				switch (device->Interfaces[0].Class) {
					case InterfaceClassAudio:               return "USB Audio Device\0" ;
					case InterfaceClassCommunications:      return "USB CDC Device\0" ;
					case InterfaceClassPhysical:            return "USB Physical Device\0";
					case InterfaceClassImage:               return "USB Imaging Device\0";
					case InterfaceClassPrinter:             return "USB Printer\0";
					case InterfaceClassMassStorage:         return "USB Mass Storage Device\0";
					case InterfaceClassCdcData:             return "USB CDC-Data Device\0";
					case InterfaceClassSmartCard:           return "USB Smart Card\0";
					case InterfaceClassContentSecurity:     return "USB Content Secuity Device\0";
					case InterfaceClassVideo:               return "USB Video Device\0";
					case InterfaceClassPersonalHealthcare:  return "USB Healthcare Device\0";
					case InterfaceClassAudioVideo:          return "USB AV Device\0";
					case InterfaceClassDiagnosticDevice:    return "USB Diagnostic Device\0";
					case InterfaceClassWirelessController:  return "USB Wireless Controller\0";
					case InterfaceClassMiscellaneous:       return "USB Miscellaneous Device\0";
					case InterfaceClassVendorSpecific:      return "Vendor Specific\0";
					case InterfaceClassHid:
						switch (device->Interfaces[0].Protocol) {
							case 1:      return "USB Keyboard\0" ;
							case 2:      return "USB Mouse\0" ;
							default:     return "USB HID\0" ;
						}
					case InterfaceClassHub:
						switch (device->Descriptor.UsbVersion) {
							case 0x210:  return "USB 2.1 Hub\0" ;
							case 0x200:  return "USB 2.0 Hub\0" ;
							case 0x110:  return "USB 1.1 Hub\0" ;
							case 0x100:  return "USB 1.0 Hub\0" ;
							default:     return "USB Hub\0" ;
						}
					default:                                return "Generic Device\0";
				}

			} else if (device->Descriptor.Class == DeviceClassVendorSpecific) {
				return "Vendor Specific\0";

			} else {
				return "Unconfigured Device\0";
			}

		default:
			return "Generic Device\0";
	}

}

//+=============================================================================
// Func: UsbAttachDevice
// Desc: Recursively enumerates a newly allocated device.
// Args: UsbDevice* device ..
// Retn: Result ..
// Note: Assign a device address and determine what the device is.
//     : If it is a hub, configure it and recurse looking for new devices
//     : If not, configure it with the default configuration.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbAttachDevice (UsbDevice* device)
{
	Result  result;
	u8      address;
	char    *buffer;

	// Store the address until it is actually assigned.
	address        = device->Number;
	device->Number = 0;
	LOG_DEBUGF("USBD: Scanning %d. %s.\n", address, SpeedToChar(device->Speed));

	result = UsbReadDeviceDescriptor(device);
	if (result != OK) {
		LOGF("USBD: Failed to read device descriptor for %d.\n", address);
		device->Number = address;
		return result;
	}

	device->Status = Default;

	if ((device->Parent != NULL) && (device->Parent->DeviceChildReset != NULL)) {
		// Reset the port for what will be the second time.
		result = device->Parent->DeviceChildReset(device->Parent, device);
		if (result != OK) {
			LOGF("USBD: Failed to reset port again for new device %s.\n", UsbGetDescription(device));
			device->Number = address;
			return result;
		}
	}

	device->Number = address;

	result = UsbSetAddress(device, address);
	if (result != OK) {
		LOGF("USBD: Failed to assign address to %#x.\n", address);
		return result;
	}

	result = UsbReadDeviceDescriptor(device);
	if (result != OK) {
		LOGF("USBD: Failed to reread device descriptor for %#x.\n", address);
		return result;
	}

	LOG_DEBUGF("USBD: Attach Device %s. Address:%d Class:%d Subclass:%d "
	           "USB:%x.%x. %d configurations, %d interfaces.\n",
	           UsbGetDescription(device), address, device->Descriptor.Class,
	           device->Descriptor.SubClass, device->Descriptor.UsbVersion >> 8,
	           (device->Descriptor.UsbVersion >> 4) & 0xF,
	           device->Descriptor.ConfigurationCount, device->Configuration.InterfaceCount);

	LOGF("USBD: Device Attached: %s.\n", UsbGetDescription(device));

	buffer = NULL;

	if (device->Descriptor.Product != 0) {
		if (buffer == NULL)  buffer = MemoryAllocate(0x100) ;

		if (buffer != NULL) {
			result = UsbReadString(device, device->Descriptor.Product, buffer, 0x100);
			if (result == OK)  LOGF("USBD:  -Product:       %s.\n", buffer) ;
		}
	}

	if (device->Descriptor.Manufacturer != 0) {
		if (buffer == NULL)  buffer = MemoryAllocate(0x100) ;

		if (buffer != NULL) {
			result = UsbReadString(device, device->Descriptor.Manufacturer, buffer, 0x100);
			if (result == OK)  LOGF("USBD:  -Manufacturer:  %s.\n", buffer) ;
		}
	}
	if (device->Descriptor.SerialNumber != 0) {
		if (buffer == NULL)  buffer = MemoryAllocate(0x100) ;

		if (buffer != NULL) {
			result = UsbReadString(device, device->Descriptor.SerialNumber, buffer, 0x100);
			if (result == OK)  LOGF("USBD:  -SerialNumber:  %s.\n", buffer) ;
		}
	}

	if (buffer != NULL) {
		MemoryDeallocate(buffer);
		buffer = NULL;
	}

	LOG_DEBUGF("USBD:  -VID:PID:       %x:%x v%d.%x\n",
	           device->Descriptor.VendorId, device->Descriptor.ProductId,
	           device->Descriptor.Version >> 8, device->Descriptor.Version & 0xFF);

	// We only support devices with 1 configuration for now.
	result = UsbConfigure(device, 0);
	if (result != OK) {
		LOGF("USBD: Failed to configure device %#x.\n", address);
		return OK;
	}

	if (device->Configuration.StringIndex != 0) {
		if (buffer == NULL)  buffer = MemoryAllocate(0x100) ;

		if (buffer != NULL) {
			result = UsbReadString(device, device->Configuration.StringIndex, buffer, 0x100);
			if (result == OK)  LOGF("USBD:  -Configuration: %s.\n", buffer) ;
		}
	}

	// If we created a buffer, free it
	if (buffer != NULL)  MemoryDeallocate(buffer) ;

	if ((device->Interfaces[0].Class < InterfaceClassAttachCount) &&
		(InterfaceClassAttach[device->Interfaces[0].Class] != NULL)) {
		result = InterfaceClassAttach[device->Interfaces[0].Class](device, 0);
		if (result != OK)
			LOGF("USBD: Could not start the driver for %s.\n", UsbGetDescription(device));

	}

	return OK;
}

//+=============================================================================
// Func: UsbAllocateDevice
// Desc: Allocates memory to a new device.
// Args: UsbDevice** device ..
// Retn: Result ..
// Note: Sets the value in the parameter 'device' to the address of a new device
//         allocated on the heap, which then has appropriate default values.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbAllocateDevice (UsbDevice** device)
{
	u32  i;

	*device = MemoryAllocate(sizeof(UsbDevice));
	if (*device == NULL)  return ErrorMemory ;

	for (i = 0; i < MaximumDevices; i++)
		if (Devices[i] == NULL) {
			Devices[i]        = *device;
			(*device)->Number = i + 1;
			break;
		}

	LOG_DEBUGF("USBD: Allocating new device, address %#x.\n", (*device)->Number);

	(*device)->Status                = Attached;
	(*device)->Error                 = None;
	(*device)->PortNumber            = 0;
	(*device)->Parent                = NULL;
	(*device)->DriverData            = NULL;
	(*device)->FullConfiguration     = NULL;
	(*device)->ConfigurationIndex    = 0xff;
	(*device)->DeviceDeallocate      = NULL;
	(*device)->DeviceDetached        = NULL;
	(*device)->DeviceCheckConnection = NULL;
	(*device)->DeviceCheckForChange  = NULL;
	(*device)->DeviceChildDetached   = NULL;
	(*device)->DeviceChildReset      = NULL;

	return OK;
}

//+=============================================================================
// Func: UsbDeallocateDevice
// Desc: Deallocates the memory and resources of a USB device.
// Args: UsbDevice* device ..
// Retn: -none-
// Note: Recursively deallocates the device and all children.
//     : Deallocates any class specific data, as well as
//         the device structure itself, and releases the device number.
// Auth: Chadderz; BlueChip
//==============================================================================
void  UsbDeallocateDevice (UsbDevice* device)
{
	LOG_DEBUGF("USBD: Deallocating device %d: %s.\n",
	           device->Number, UsbGetDescription(device));

	if (device->DeviceDetached != NULL)    device->DeviceDetached(device) ;
	if (device->DeviceDeallocate != NULL)  device->DeviceDeallocate(device) ;

	if ((device->Parent != NULL) && (device->Parent->DeviceChildDetached != NULL))
		device->Parent->DeviceChildDetached(device->Parent, device);

	if (((device->Status == Addressed) || (device->Status == Configured)) &&
	    ((device->Number > 0) && (device->Number <= MaximumDevices) &&
	    (Devices[device->Number - 1] == device)))
			Devices[device->Number - 1] = NULL;

	if (device->FullConfiguration != NULL)
		MemoryDeallocate((void*)device->FullConfiguration);  //! we are casting off "volatile"

	MemoryDeallocate(device);
}

//+=============================================================================
// Func: UsbAttachRootHub
// Desc: Scans the bus for hubs.
// Args: -none-
// Retn: Result ..
// Note: Enumerates the devices on the bus, performing necessary initialisation
//         and building a device tree for later reference.
// Auth: Chadderz; BlueChip
//==============================================================================
Result  UsbAttachRootHub (void)
{
	Result result;
	UsbDevice *rootHub;

	LOG_DEBUG("USBD: Scanning for devices.\n");

	if (Devices[0] != NULL)  UsbDeallocateDevice(Devices[0]) ;

	result = UsbAllocateDevice(&rootHub);
	if (result != OK)
		return result;

	Devices[0]->Status = Powered;

	result = UsbAttachDevice(Devices[0]);

	return result;
}

//+=============================================================================
// Func: UsbCheckForChange
// Desc: Scans the entire USB tree for changes.
// Args: -none-
// Retn: -none-
// Note: Recursively calls HubCheckConnection()
//         on all ports on all hubs connected to the root hub.
// Auth: Chadderz; BlueChip
//==============================================================================
void  UsbCheckForChange (void)
{
	if ((Devices[0] != NULL) && (Devices[0]->DeviceCheckForChange != NULL))
		Devices[0]->DeviceCheckForChange(Devices[0]);
}
